﻿using Newtonsoft.Json.Linq;
using System;
using System.Text;

namespace localfolder
{
    class Program
    {
        static System.IO.Stream stdout;
        static System.IO.Stream stdin;
        static bool bExit = false;
        static DateTime lastActive = DateTime.Now;
        static void Active()
        {
            lastActive = DateTime.Now;
        }
        static void Main(string[] args)
        {
            stdout = Console.OpenStandardOutput();
            stdin = Console.OpenStandardInput();
            System.Threading.Thread thread = new System.Threading.Thread(WriteThread);
            //thread.IsBackground = true;
            thread.Start();

            WriteHelloMsg(-1);

            try
            {
                byte[] lendata = new byte[4];
                int lendatareadpos = 0;
                byte[] data = null;
                int datareadpos = 0;
                while (!bExit)
                {
                    //如果长度没读够，读长度
                    if (lendatareadpos < 4)
                    {
                        Active();
                        lendatareadpos += stdin.Read(lendata, lendatareadpos, 4 - lendatareadpos);

                        if (lendatareadpos == 4)
                        {
                            var len = BitConverter.ToUInt32(lendata, 0);
                            data = new byte[len];
                            datareadpos = 0;
                        }
                    }
                    else if (datareadpos < data.Length)
                    {//如果长度够了，读data
                        Active();
                        datareadpos += stdin.Read(data, datareadpos, data.Length - datareadpos);
                        if (datareadpos == data.Length)
                        {
                            var text = System.Text.Encoding.UTF8.GetString(data);
                            JObject json = null;
                            try
                            {
                                json = JObject.Parse(text);
                                OnRead(json);
                            }
                            catch (Exception err)
                            {
                                WriteErrorMsg(err.Message + "====" + err.StackTrace + "====" + text, -1);
                            }
                            lendatareadpos = 0;
                            data = null;
                            datareadpos = 0;
                        }
                    }
                    System.Threading.Thread.Sleep(1);
                
                }
            }
            catch (Exception err)
            {
                WriteErrorMsg(err.Message + "====" + err.StackTrace, -1);
            }
        }
        static System.Collections.Concurrent.ConcurrentQueue<byte[]> senddata = new System.Collections.Concurrent.ConcurrentQueue<byte[]>();
        static void WriteThread()
        {
            while (!bExit || senddata.Count > 0)
            {
                if (senddata.TryDequeue(out byte[] data))
                {
                    stdout.Write(data, 0, data.Length);
                    Active();
                }
                System.Threading.Thread.Sleep(1);
                if ((DateTime.Now - lastActive).TotalSeconds > 10.0)
                {
                    bExit = true;
                    Environment.Exit(0);
                }
            }
        }

        static void OnRead(JObject json)
        {
            if (json.ContainsKey("msgid") == false || json["msgid"].Type != JTokenType.Integer)
            {
                WriteErrorMsg("error msgid ,must a int value", -1);
                return;
            }
            if (json.ContainsKey("cmd") == false || json["cmd"].Type != JTokenType.String)
            {
                WriteErrorMsg("error msg ,must a string value", -1);
                return;
            }
            var msgid = (int)json["msgid"];
            var cmd = (string)json["cmd"];
            switch (cmd)
            {
                case "ping":
                    WriteMsg("ping", msgid);
                    break;
                case "exit":
                    bExit = true;
                    break;
                case "access.request":
                    OnAccessRequest(msgid, json);
                    break;
                case "access.close":
                    OnAccessClose(msgid, json);
                    break;
                case "folder.list":
                    OnFolderList(msgid, json);
                    break;
                case "folder.delete":
                    OnFolderDelete(msgid, json);
                    break;
                case "folder.create":
                    OnFolderCreate(msgid, json);
                    break;
                case "folder.move":
                    OnFolderMove(msgid, json);
                    break;
                case "file.write":
                    OnFileWrite(msgid, json);
                    break;
                case "file.delete":
                    OnFileDelete(msgid, json);
                    break;
                case "file.move":
                    OnFileMove(msgid, json);
                    break;
                case "file.read":
                    OnFileRead(msgid, json);
                    break;
                default:
                    WriteErrorMsg("unknown msg", msgid);
                    break;
            }
        }
        static string RootPath;
        static void OnAccessRequest(int msgid, JObject json)
        {
            var _param = new JObject();
            if (json.ContainsKey("path") == false || json["path"].Type != JTokenType.String)
            {
                _param["succ"] = false;
                _param["error"] = "error msgid ,must a int value";
                WriteMsg("access.request", msgid, _param);
                return;
            }
            string path = (string)json["path"];
            if (System.IO.Directory.Exists(path))
            {
                RootPath = path;
                _param["succ"] = true;
                WriteMsg("access.request", msgid, _param);
                return;
            }
            else
            {
                _param["succ"] = false;
                _param["error"] = "path:" + path + " is not exists.";
                WriteMsg("access.request", msgid, _param);
                return;
            }
        }
        static void OnAccessClose(int msgid, JObject json)
        {

            if (RootPath == null)
            {
                var _param = new JObject();
                _param["succ"] = false;
                _param["error"] = "not open";
                WriteMsg("access.close", msgid, _param);
            }
            else
            {
                RootPath = null;
                var _param = new JObject();
                _param["succ"] = true;
                WriteMsg("access.close", msgid, _param);
            }
        }
        static void OnFolderList(int msgid, JObject json)
        {
            if (json.ContainsKey("path") == false || json["path"].Type != JTokenType.String)
            {
                var _param = new JObject();
                _param["succ"] = false;
                _param["error"] = "error path, must a string value";
                WriteMsg("folder.list", msgid, _param);
                return;
            }
            try
            {
                var path = (string)json["path"];
                var targetpath = System.IO.Path.Combine(RootPath, path);
                var dir = new System.IO.DirectoryInfo(targetpath);
                var list = new JArray();
                foreach (var d in dir.GetDirectories())
                {
                    var item = new JObject();
                    item["name"] = d.Name;
                    item["time"] = d.LastWriteTime.ToLongTimeString();
                    item["type"] = "dir";
                    list.Add(item);
                }
                foreach (var d in dir.GetFiles())
                {
                    var item = new JObject();
                    item["name"] = d.Name;
                    item["size"] = d.Length;
                    item["type"] = "file";
                    item["time"] = d.LastWriteTime.ToLongTimeString();
                    list.Add(item);
                }

                var _param = new JObject();
                _param["succ"] = true;
                _param["list"] = list;
                _param["path"] = path;
                WriteMsg("folder.list", msgid, _param);

            }
            catch (Exception err)
            {
                var _param = new JObject();
                _param["succ"] = false;
                _param["error"] = err.Message + "||" + err.StackTrace;
                WriteMsg("folder.list", msgid, _param);
            }
        }
        static void OnFolderCreate(int msgid, JObject json)
        {
            if (json.ContainsKey("path") == false || json["path"].Type != JTokenType.String)
            {
                var _param = new JObject();
                _param["succ"] = false;
                _param["error"] = "error path, must a string value";
                WriteMsg("folder.create", msgid, _param);
                return;
            }
            try
            {
                var path = (string)json["path"];
                var targetpath = System.IO.Path.Combine(RootPath, path);
                System.IO.Directory.CreateDirectory(targetpath);

                var _param = new JObject();
                _param["succ"] = true;
                _param["path"] = path;
                WriteMsg("folder.create", msgid, _param);

            }
            catch (Exception err)
            {
                var _param = new JObject();
                _param["succ"] = false;
                _param["error"] = err.Message + "||" + err.StackTrace;
                WriteMsg("folder.create", msgid, _param);
            }
        }
        static void OnFolderDelete(int msgid, JObject json)
        {
            if (json.ContainsKey("path") == false || json["path"].Type != JTokenType.String)
            {
                var _param = new JObject();
                _param["succ"] = false;
                _param["error"] = "error path, must a string value";
                WriteMsg("folder.delete", msgid, _param);
                return;
            }
            try
            {
                var path = (string)json["path"];
                var targetpath = System.IO.Path.Combine(RootPath, path);
                System.IO.Directory.Delete(targetpath);

                var _param = new JObject();
                _param["succ"] = true;
                _param["path"] = path;
                WriteMsg("folder.delete", msgid, _param);

            }
            catch (Exception err)
            {
                var _param = new JObject();
                _param["succ"] = false;
                _param["error"] = err.Message + "||" + err.StackTrace;
                WriteMsg("folder.delete", msgid, _param);
            }
        }
        static void OnFolderMove(int msgid, JObject json)
        {
            if (json.ContainsKey("path") == false || json["path"].Type != JTokenType.String)
            {
                var _param = new JObject();
                _param["succ"] = false;
                _param["error"] = "error path, must a string value";
                WriteMsg("folder.move", msgid, _param);
                return;
            }
            if (json.ContainsKey("newpath") == false || json["newpath"].Type != JTokenType.String)
            {
                var _param = new JObject();
                _param["succ"] = false;
                _param["error"] = "error newpath, must a string value";
                WriteMsg("folder.move", msgid, _param);
                return;
            }
            try
            {
                var path = (string)json["path"];
                var newpath = (string)json["newpath"];
                var targetpath = System.IO.Path.Combine(RootPath, path);
                var targetnewpath = System.IO.Path.Combine(RootPath, newpath);
                System.IO.Directory.Move(targetpath, targetnewpath);

                var _param = new JObject();
                _param["succ"] = true;
                _param["path"] = path;
                _param["newpath"] = newpath;
                WriteMsg("folder.move", msgid, _param);

            }
            catch (Exception err)
            {
                var _param = new JObject();
                _param["succ"] = false;
                _param["error"] = err.Message + "||" + err.StackTrace;
                WriteMsg("folder.move", msgid, _param);
            }
        }
        static void OnFileWrite(int msgid, JObject json)
        {
            if (json.ContainsKey("path") == false || json["path"].Type != JTokenType.String)
            {
                var _param = new JObject();
                _param["succ"] = false;
                _param["error"] = "error path, must a string value";
                WriteMsg("file.write", msgid, _param);
                return;
            }
            if (json.ContainsKey("format") == false || json["format"].Type != JTokenType.String)
            {
                var _param = new JObject();
                _param["succ"] = false;
                _param["error"] = "error format, must a string value";
                WriteMsg("file.write", msgid, _param);
                return;
            }
            if (json.ContainsKey("data") == false || json["data"].Type != JTokenType.String)
            {
                var _param = new JObject();
                _param["succ"] = false;
                _param["error"] = "error format, must a string value";
                WriteMsg("file.write", msgid, _param);
                return;
            }
            try
            {
                var format = (string)json["format"];
                var strdata = (string)json["data"];
                byte[] bindata = String2Data(strdata, format);

                var path = (string)json["path"];
                var targetpath = System.IO.Path.Combine(RootPath, path);
                System.IO.File.WriteAllBytes(targetpath, bindata);
                var _param = new JObject();
                _param["succ"] = true;
                _param["path"] = path;
                WriteMsg("file.write", msgid, _param);

            }
            catch (Exception err)
            {
                var _param = new JObject();
                _param["succ"] = false;
                _param["error"] = err.Message + "||" + err.StackTrace;
                WriteMsg("file.write", msgid, _param);
            }
        }
        static void OnFileDelete(int msgid, JObject json)
        {
            if (json.ContainsKey("path") == false || json["path"].Type != JTokenType.String)
            {
                var _param = new JObject();
                _param["succ"] = false;
                _param["error"] = "error path, must a string value";
                WriteMsg("file.delete", msgid, _param);
                return;
            }
            try
            {
                var path = (string)json["path"];
                var targetpath = System.IO.Path.Combine(RootPath, path);
                System.IO.File.Delete(targetpath);

                var _param = new JObject();
                _param["succ"] = true;
                _param["path"] = path;
                WriteMsg("file.delete", msgid, _param);

            }
            catch (Exception err)
            {
                var _param = new JObject();
                _param["succ"] = false;
                _param["error"] = err.Message + "||" + err.StackTrace;
                WriteMsg("file.delete", msgid, _param);
            }
        }
        static void OnFileMove(int msgid, JObject json)
        {
            if (json.ContainsKey("path") == false || json["path"].Type != JTokenType.String)
            {
                var _param = new JObject();
                _param["succ"] = false;
                _param["error"] = "error path, must a string value";
                WriteMsg("file.move", msgid, _param);
                return;
            }
            if (json.ContainsKey("newpath") == false || json["newpath"].Type != JTokenType.String)
            {
                var _param = new JObject();
                _param["succ"] = false;
                _param["error"] = "error newpath, must a string value";
                WriteMsg("file.move", msgid, _param);
                return;
            }
            try
            {
                var path = (string)json["path"];
                var newpath = (string)json["newpath"];
                var targetpath = System.IO.Path.Combine(RootPath, path);
                var targetnewpath = System.IO.Path.Combine(RootPath, newpath);
                System.IO.File.Move(targetpath, targetnewpath);

                var _param = new JObject();
                _param["succ"] = true;
                _param["path"] = path;
                WriteMsg("file.move", msgid, _param);

            }
            catch (Exception err)
            {
                var _param = new JObject();
                _param["succ"] = false;
                _param["error"] = err.Message + "||" + err.StackTrace;
                WriteMsg("file.move", msgid, _param);
            }
        }
        static void OnFileRead(int msgid, JObject json)
        {
            if (json.ContainsKey("path") == false || json["path"].Type != JTokenType.String)
            {
                var _param = new JObject();
                _param["succ"] = false;
                _param["error"] = "error path, must a string value";
                WriteMsg("file.read", msgid, _param);
                return;
            }
            if (json.ContainsKey("format") == false || json["format"].Type != JTokenType.String)
            {
                var _param = new JObject();
                _param["succ"] = false;
                _param["error"] = "error format, must a string value";
                WriteMsg("file.read", msgid, _param);
                return;
            }
            try
            {
                var format = (string)json["format"];
                var path = (string)json["path"];
                var targetpath = System.IO.Path.Combine(RootPath, path);
                var f = new System.IO.FileInfo(targetpath);
                int start = 0;
                if (json.ContainsKey("start") == false && json["start"].Type == JTokenType.Integer)
                {
                    start = (int)json["start"];
                }
                int length = (int)f.Length;
                if (json.ContainsKey("length") == false && json["length"].Type == JTokenType.Integer)
                {
                    var jlen = (int)json["length"];
                    if (start + jlen > length)
                        throw new Exception("error file size");
                    length = jlen;
                }
                byte[] data = new byte[length];
                using (System.IO.Stream s = System.IO.File.OpenRead(targetpath))
                {
                    s.Seek(start, System.IO.SeekOrigin.Begin);
                    s.Read(data, 0, length);
                }

                var _param = new JObject();
                _param["succ"] = true;
                _param["path"] = path;
                _param["format"] = format;
                _param["start"] = start;
                _param["length"] = length;
                _param["data"] = Data2String(data, format);
                WriteMsg("file.read", msgid, _param);

            }
            catch (Exception err)
            {
                var _param = new JObject();
                _param["succ"] = false;
                _param["error"] = err.Message + "||" + err.StackTrace;
                WriteMsg("file.read", msgid, _param);
            }

        }
        static void WriteErrorMsg(string errorinfo, int msgid)
        {
            var json = new JObject();
            json["msg"] = errorinfo;
            WriteMsg("error", msgid, json);
        }
        static void WriteHelloMsg(int msgid)
        {
            var json = new JObject();
            json["msg"] = "Hello world!";
            WriteMsg("sayhi", msgid, json);
        }
        static void WriteMsg(string cmd, int msgid = -1, JObject exParam = null)
        {
            var json = new JObject();
            json["native"] = "localfolder";
            json["cmd"] = cmd;
            json["msgid"] = msgid;
            if (exParam != null)
            {
                foreach (var item in exParam)
                {
                    json[item.Key] = item.Value;
                }
            }

            var jsonStr = json.ToString();
            var outdata = System.Text.Encoding.UTF8.GetBytes(jsonStr);
            var lendata = BitConverter.GetBytes((uint)outdata.Length);
            senddata.Enqueue(lendata);
            senddata.Enqueue(outdata);
        }
        static string Data2String(byte[] data, string format)
        {
            if (format == "string")
            {
                return System.Text.Encoding.UTF8.GetString(data);
            }
            else if (format == "base64")
            {
                return Convert.ToBase64String(data);
            }
            else if (format == "hexstr")
            {
                return Hex2Str(data);
            }
            else
            {
                throw new Exception("unknown format.");
            }
        }
        static byte[] String2Data(string strdata, string format)
        {
            byte[] bindata = null;
            if (format == "string")
            {
                bindata = System.Text.Encoding.UTF8.GetBytes(strdata);
            }
            else if (format == "base64")
            {
                bindata = Convert.FromBase64String(strdata);
            }
            else if (format == "hexstr")
            {
                bindata = Str2Hex(strdata);
            }
            else
            {
                throw new Exception("unknown format.");
            }
            return bindata;
        }

        static string Hex2Str(byte[] data)
        {
            StringBuilder sb = new StringBuilder();
            foreach (var b in data)
            {
                sb.Append(b.ToString("X02"));
            }
            return sb.ToString();
        }
        static byte[] Str2Hex(string hexstr)
        {
            if (hexstr.IndexOf("0x") == 0 || hexstr.IndexOf("0X") == 0)
                hexstr = hexstr.Substring(2);
            byte[] data = new byte[hexstr.Length / 2];
            for (var i = 0; i < data.Length; i++)
            {
                data[i] = byte.Parse(hexstr.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber);
            }
            return data;
        }
    }
}